package com.vhall.component.service.pay.impl;

import cn.hutool.core.net.URLDecoder;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayClient;
import com.alipay.api.request.AlipayTradePrecreateRequest;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.alipay.api.response.AlipayTradeWapPayResponse;
import com.ijpay.alipay.AliPayApiConfig;
import com.vhall.component.entity.pay.entity.PaymentOrderEntity;
import com.vhall.component.framework.common.constant.CommonConstant;
import com.vhall.component.framework.common.exception.BusinessException;
import com.vhall.component.plugin.common.exception.BizErrorCode;
import com.vhall.component.service.pay.SettingService;
import com.vhall.component.service.pay.constant.Channel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * @author xuyongzheng
 * @since 2020/10/22
 **/
@Slf4j
@Component
public class AliPayService {

    private static final String PAY_ALI_NOTIFY = "/v4/pay/ali-notify";
    private static final String OUT_TRADE_NO = "out_trade_no";
    private static final String SUBJECT = "subject";
    private static final String SIGN_TYPE = "sign_type";


    @Value("${vhall.pay.domain}")
    private String domain;

    @Value("${vhall.alipay.domain}")
    private String aliGateWay;

    @Autowired
    private SettingService settingService;

    /**
     * 返回的是 QRCODE 地址
     *
     * @param entity entity
     * @return qr code url
     */
    public String qrPayOrder(PaymentOrderEntity entity) {
        AlipayClient client = buildClient(entity.getAppId(), entity.getServiceCode());
        try {

            AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
            request.setNotifyUrl(domain + PAY_ALI_NOTIFY);
            Map<String, Object> bizContent = new HashMap<>(4);
            bizContent.put(OUT_TRADE_NO, entity.getBizOrderNo());
            bizContent.put("total_amount", entity.getTotalFee());
            bizContent.put(SUBJECT, entity.getTitle());
            request.setBizContent(JSON.toJSONString(bizContent));

            AlipayTradePrecreateResponse response = client.execute(request);
            if (response.isSuccess()) {
                return response.getQrCode();

            } else {
                log.error("支付宝QR-PAY 预下单失败，response:[{}]", JSON.toJSONString(response));
                throw new BusinessException(BizErrorCode.COMP_PAY_ORDER_FAIL);
            }

        } catch (Exception ex) {
            log.error("支付宝QR-PAY下单异常:", ex);
            throw new BusinessException(BizErrorCode.COMP_PAY_ORDER_FAIL);
        }
    }

    /**
     * H5 支付
     *
     * @param entity entity
     * @return pay url
     */
    public String wapPayOrder(PaymentOrderEntity entity) {
        AlipayClient client = buildClient(entity.getAppId(), entity.getServiceCode());
        try {
            AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
            request.setNotifyUrl(domain + PAY_ALI_NOTIFY);

            Map<String, Object> bizContent = new HashMap<>(4);
            bizContent.put(OUT_TRADE_NO, entity.getBizOrderNo());
            bizContent.put("total_amount", entity.getTotalFee());
            bizContent.put(SUBJECT, entity.getTitle());
            bizContent.put("product_code", "QUICK_WAP_WAY");
            request.setBizContent(JSON.toJSONString(bizContent));

            request.setReturnUrl(StringUtils.isEmpty(entity.getShowUrl()) ? "" : entity.getShowUrl());

            AlipayTradeWapPayResponse response = client.pageExecute(request, "GET");

            if (response.isSuccess()) {
                return response.getBody();
            } else {
                log.error("支付宝H5-PAY预下单失败，response:[{}]", JSON.toJSONString(response));
                throw new BusinessException(BizErrorCode.COMP_PAY_ORDER_FAIL);
            }

        } catch (Exception ex) {
            log.error("支付宝H5-PAY下单异常:", ex);
            throw new BusinessException(BizErrorCode.COMP_PAY_ORDER_FAIL);
        }
    }

    /**
     * 即时到账交易接口
     * </br>
     * 获取收银台 支付链接，使用支付宝老版本接口
     * </br>
     * https://opendocs.alipay.com/open/62/103738
     *
     * @param entity entity
     * @deprecated 收银台的方式过时了
     */
    public String cashierPay(PaymentOrderEntity entity) {

        Map<String, String> setting = settingService.mapBy(entity.getAppId(), Channel.ALIPAY, entity.getServiceCode());

        if (null == setting || setting.size() == 0) {
            throw new BusinessException(BizErrorCode.COMP_PAY_PARAM_UNSET);
        }

        try {
            SortedMap<String, Object> params = new TreeMap<>();
            params.put("service", "create_direct_pay_by_user");
            params.put("partner", setting.get("partner"));
            params.put("_input_charset", CommonConstant.UTF8);
            params.put(SUBJECT, entity.getTitle());
            params.put(OUT_TRADE_NO, entity.getBizOrderNo());
            params.put("total_fee", entity.getTotalFee());
            params.put("payment_type", 1);
            params.put("seller_email", setting.get("seller_email"));
            params.put("notify_url", domain + PAY_ALI_NOTIFY);
            params.put("return_url", entity.getShowUrl() == null ? "" : entity.getShowUrl());
            params.put(SIGN_TYPE, "MD5");

            String q = singedParam(params, setting.get("md5Key"));

            return "http://mapi.alipay.com/gateway.do?" + q;

        } catch (Exception ex) {
            log.error("支付宝CASHIER下单异常:", ex);
            throw new BusinessException(BizErrorCode.COMP_PAY_ORDER_FAIL);
        }
    }

    private AlipayClient buildClient(String appId, String sCode) {

        Map<String, String> setting = settingService.mapBy(appId, Channel.ALIPAY, sCode);

        if (null == setting || setting.size() == 0) {
            throw new BusinessException(BizErrorCode.COMP_PAY_PARAM_UNSET);
        }

        return AliPayApiConfig.builder()
                .setServiceUrl(aliGateWay)
                .setAppId(setting.get("appid"))
                .setAliPayPublicKey(setting.get("rsaPublicKey"))
                .setPrivateKey(setting.get("rsaPrivateKeyPKCS8"))
//                .setPrivateKey(setting.get("rsaPrivateKey"))
                .setSignType("RSA2")
                .setCharset(CommonConstant.UTF8)
                // 普通公钥方式
                .build()
                .getAliPayClient();
    }

    private String singedParam(SortedMap<String, Object> param, String key) {
        if (param.containsKey(SIGN_TYPE)) {
            param.remove(SIGN_TYPE);
        }
        if (param.containsKey("sign")) {
            param.remove("sign");
        }
        StringBuilder q = new StringBuilder();

        StringBuilder sb = new StringBuilder();
        param.keySet().forEach(k -> {
            if (StringUtils.isNotEmpty(param.get(k).toString())) {
                sb.append(k).append("=").append(URLDecoder.decode(param.get(k).toString(), StandardCharsets.UTF_8)).append("&");
            }
            q.append(k).append("=").append(URLDecoder.decode(param.get(k).toString(), StandardCharsets.UTF_8)).append("&");
        });
        String pre = StringUtils.removeEnd(sb.toString(), "&");
        String sign = SecureUtil.md5(pre + key);
        return q.toString() + "sign_type=MD5&sign=" + sign;
    }
}
